Nginx reverse proxy
Nginx terminates TLS and forwards traffic to localhost Python services. Only Nginx listens on ports 80 and 443.
Subdomain routing (preferred)
Each API gets its own subdomain:
customer.example.com → 127.0.0.1:8001
reports.example.com → 127.0.0.1:8002
Advantages: clean URLs, simpler CORS, independent SSL SAN entries, easier OpenAPI at /docs.
File locations
| File | Purpose |
|---|---|
/etc/nginx/sites-available/<api-name> | Server block definition |
/etc/nginx/sites-enabled/<api-name> | Symlink to sites-available |
/etc/nginx/nginx.conf | Global settings (usually leave default) |
Enable a site:
sudo ln -sf /etc/nginx/sites-available/<api-name> /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
HTTP-only (LAN testing)
Use default_server on port 80 so http://<lan-ip>/health and /docs work without DNS.
Full runbook: 11-nginx-lan-setup.md
Template: templates/nginx/dwd-api-fastapi-lan.conf
For subdomain production, use templates/nginx-site.template.
HTTPS with existing certificate
Add to the server block (paths depend on your cert provider):
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
# Recommended baseline
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
Redirect HTTP to HTTPS:
server {
listen 80;
listen [::]:80;
server_name <subdomain>;
return 301 https://$host$request_uri;
}
Record actual certificate paths here when configured:
| Item | Path |
|---|---|
| Full chain | TBD |
| Private key | TBD |
Proxy headers
The template sets headers expected by most Python frameworks:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
FastAPI/Starlette apps behind HTTPS should trust forwarded proto if generating absolute URLs — often via middleware or root_path when needed.
WebSockets
If an API uses WebSockets, the template includes:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Timeouts
Default Nginx proxy timeouts may be too short for long-running requests. Adjust in the location block if needed:
proxy_read_timeout 300s;
proxy_send_timeout 300s;
Verification
sudo nginx -t
curl -I http://127.0.0.1/ -H "Host: <subdomain>"
curl -I https://<subdomain>/health
Multiple APIs
Each API gets its own file under sites-available/. Do not combine unrelated APIs into one server block unless using path-based routing (not recommended for this server).
Path-based alternative (not default here):
location /customer/ {
proxy_pass http://127.0.0.1:8001/;
}
Requires application URL prefix configuration and is harder to maintain than subdomains.